package rt2

import (
	"bytes"
	"encoding/binary"
	"errors"
	"sync"

	"github.com/sigurn/crc16"
)

var (
	errDataLengthNotEnough        = errors.New("data length not enough")
	errNoValidFrameHeader         = errors.New("no valid frame header")
	errCallbackIsNil              = errors.New("callback is nil")
	errFrameTypeAlreadyRegistered = errors.New("frame type already registered")
	errFrameTypeNotRegistered     = errors.New("frame type not registered")
	errChecksumMismatch           = errors.New("checksum mismatch")
)

type FrameType = int32

type Frame struct {
	header     byte // 0xAA
	Type       int32
	Seq        int32
	PayloadLen int32
	Payload    []byte
	Checksum   uint16

	// 不参与序列化
	Data  any
	Error error
}

func (f Frame) Size() int {
	return f.HeaderLen() + len(f.Payload) + 2
}
func (f Frame) HeaderLen() int {
	return 1 + 4*3
}

func (f Frame) PayloadLenLen() int {
	return 4
}

func (f Frame) CalcuChecksumLen() int {
	return f.Size() - 2
}

func (f Frame) Equal(f1 Frame) bool {
	if f.Size() != f1.Size() {
		return false
	}
	if f.header != f1.header {
		return false
	}
	if f.Type != f1.Type {
		return false
	}
	if f.Seq != f1.Seq {
		return false
	}
	if f.PayloadLen != f1.PayloadLen {
		return false
	}
	if !bytes.Equal(f.Payload, f1.Payload) {
		return false
	}
	if f.Checksum != f1.Checksum {
		return false
	}
	return true
}

type Decoder struct {
	header    byte
	decCbMap  map[FrameType]decodeCallback
	lock      sync.Mutex
	buf       []byte
	bufSize   int
	seq       int32
	byteOrder binary.ByteOrder
}

// 解析一个切片中的帧头，pos为出现的位置，若解析失败则为下一个header可能出现的位置
func (d *Decoder) DecodeHeader(data []byte) (pos int, err error) {
	if len(data) < 1 {
		pos = 0
		err = errDataLengthNotEnough
		return
	}
	pos = bytes.IndexByte(data, d.header)
	if pos == -1 {
		pos = len(data)
		err = errNoValidFrameHeader
		return
	}
	return
}

func NewDecoder(header byte, bufSize int) *Decoder {
	return NewDecoderRaw(header, bufSize, binary.LittleEndian)
}

func NewDecoderRaw(header byte, bufSize int, order binary.ByteOrder) *Decoder {
	dec := &Decoder{
		header:    header,
		decCbMap:  make(map[FrameType]decodeCallback),
		buf:       make([]byte, 0, bufSize),
		bufSize:   bufSize,
		byteOrder: order,
	}
	return dec
}

type decodeCallback func(f *Frame) (any, error)

func (d *Decoder) RegisterDecodeCallback(ft FrameType, f decodeCallback) error {
	if f == nil {
		return errCallbackIsNil
	}
	d.lock.Lock()
	defer d.lock.Unlock()
	if _, ok := d.decCbMap[ft]; ok {
		return errFrameTypeAlreadyRegistered
	}
	d.decCbMap[ft] = f
	return nil
}

func (d *Decoder) genSeq() int32 {
	d.lock.Lock()
	defer d.lock.Unlock()
	d.seq++
	return d.seq
}

func (d *Decoder) PackFrame(ft FrameType, payload []byte) *Frame {
	return d.PackFrameWithSeq(ft, payload, d.genSeq())
}

func (d *Decoder) PackFrameWithSeq(ft FrameType, payload []byte, seq int32) *Frame {
	f := &Frame{
		header:     d.header,
		Type:       ft,
		Seq:        seq,
		PayloadLen: int32(len(payload)),
		Payload:    payload,
	}
	f.Checksum = calcu_checksum(d.Encode(f)[:f.CalcuChecksumLen()])
	return f
}

func (d *Decoder) PackAndEncode(ft FrameType, payload []byte) []byte {
	f := d.PackFrame(ft, payload)
	return d.Encode(f)
}

func (d *Decoder) PackAndEncodeWithSeq(ft FrameType, payload []byte, seq int32) []byte {
	f := d.PackFrameWithSeq(ft, payload, seq)
	return d.Encode(f)
}

func (d *Decoder) Encode(f *Frame) []byte {
	if f == nil {
		panic("Encode failed: frame is nil")
	}

	var buf bytes.Buffer

	// 写入header
	buf.WriteByte(f.header)

	// 写入Type
	binary.Write(&buf, d.byteOrder, f.Type)

	// 写入Seq
	binary.Write(&buf, d.byteOrder, f.Seq)

	// 写入PayloadLen
	binary.Write(&buf, d.byteOrder, f.PayloadLen)

	// 写入Payload
	buf.Write(f.Payload)

	// 计算并写入Checksum (CRC-16)
	data := buf.Bytes()
	checksum := calcu_checksum(data)
	binary.Write(&buf, d.byteOrder, checksum)

	return buf.Bytes()
}

func calcu_checksum(data []byte) uint16 {
	return crc16.Checksum(data, crc16.MakeTable(crc16.CRC16_MODBUS))
}

func (d *Decoder) decode(f *Frame, buf []byte) error {
	reader := bytes.NewReader(buf)

	// 读取header
	header := make([]byte, 1)
	if _, err := reader.Read(header); err != nil {
		return err
	}
	f.header = header[0]

	// 读取Type
	if err := binary.Read(reader, d.byteOrder, &f.Type); err != nil {
		return err
	}

	// 读取Seq
	if err := binary.Read(reader, d.byteOrder, &f.Seq); err != nil {
		return err
	}

	// 读取PayloadLen
	if err := binary.Read(reader, d.byteOrder, &f.PayloadLen); err != nil {
		return err
	}

	// 读取Payload
	payload := make([]byte, f.PayloadLen)
	if cnt, err := reader.Read(payload); err != nil || cnt != int(f.PayloadLen) {
		return errDataLengthNotEnough
	}
	f.Payload = payload

	// 读取Checksum
	if err := binary.Read(reader, d.byteOrder, &f.Checksum); err != nil {
		return err
	}

	// 验证Checksum
	calculatedChecksum := calcu_checksum(buf[:f.CalcuChecksumLen()])
	if calculatedChecksum != f.Checksum {
		return errChecksumMismatch
	}

	return nil
}

func (d *Decoder) RecvAndDecode(data []byte) (res []*Frame) {
	d.buf = append(d.buf, data...)

	for {
		pos, err := d.DecodeHeader(d.buf)
		d.buf = d.buf[pos:]
		if err != nil {
			return
		}

		// 空帧，payload长度为0
		f := &Frame{}
		// 检查剩余数据是否足够解析整个帧
		if len(d.buf) < f.Size() {
			return
		}

		// 解析PayloadLen
		payloadLen := int(binary.LittleEndian.Uint32(d.buf[f.HeaderLen()-f.PayloadLenLen() : f.HeaderLen()]))

		totalFrameSize := f.Size() + payloadLen

		// 错误数据导致超过bufSize的帧，丢弃帧头
		if totalFrameSize > d.bufSize {
			d.buf = d.buf[1:]
			continue
		}

		if len(d.buf) < totalFrameSize {
			return
		}

		// 解析帧
		frameData := d.buf[:totalFrameSize]
		// 丢弃解析完成的数据
		d.buf = d.buf[totalFrameSize:]

		if err := d.decode(f, frameData); err != nil {
			f.Error = err
			res = append(res, f)
			continue
		}

		if callback, ok := d.decCbMap[f.Type]; ok {
			f.Data, f.Error = callback(f)
		} else {
			f.Error = errFrameTypeNotRegistered
		}

		res = append(res, f)
	}
}
